Setup

Load libraries

library(ggplot2)
library(tidyr)
library(dplyr)
library(Matrix)
library(Seurat)
library(cowplot)
library(patchwork)

# parallelization
library(future)
options(future.globals.maxSize= +Inf)
plan()
sequential:
- args: function (expr, envir = parent.frame(), substitute = TRUE, lazy = FALSE, seed = NULL, globals = TRUE, local = TRUE, earlySignal = FALSE, label = NULL, ...)
- tweaked: FALSE
- call: NULL

Process Human Data

import_remote_data <- function(file_url, type = "table", header = FALSE) {
  con <- gzcon(url(file_url))
  txt <- readLines(con)
  if (type == "MM") { return (readMM(textConnection(txt))) }
  if (type == "table") { return (read.table(textConnection(txt), header = header)) }
}
count_matrix_URL <- "https://ftp.ncbi.nlm.nih.gov/geo/series/GSE137nnn/GSE137537/suppl/GSE137537_counts.mtx.gz"
gene_names_URL <- "https://ftp.ncbi.nlm.nih.gov/geo/series/GSE137nnn/GSE137537/suppl/GSE137537_gene_names.txt.gz"
sample_annotations_URL <- "https://ftp.ncbi.nlm.nih.gov/geo/series/GSE137nnn/GSE137537/suppl/GSE137537_sample_annotations.tsv.gz"

human.count_matrix <- as.matrix(import_remote_data(count_matrix_URL, type = "MM"))
human.gene_names <- import_remote_data(gene_names_URL, type = "table")
human.sample_annotations <- import_remote_data(sample_annotations_URL, type = "table", header = TRUE)
human_ret_seurat
An object of class Seurat 
19712 features across 20091 samples within 1 assay 
Active assay: RNA (19712 features, 0 variable features)

Process Mouse Data

mouse.data <- Read10X(data.dir = "filtered_feature_bc_matrix")
dimnames(mouse.data)[[1]] <- tolower(dimnames(mouse.data)[[1]])
dimnames(mouse.data)[[2]] <- tolower(dimnames(mouse.data)[[2]])
mouse_ret_seurat <- CreateSeuratObject(counts = mouse.data, 
                                       project = "mouse_ret", 
                                       min.cells = 3, 
                                       min.features = 200)
mouse_ret_seurat
An object of class Seurat 
16424 features across 4510 samples within 1 assay 
Active assay: RNA (16424 features, 0 variable features)

Process Primate Data

url=https://ftp.ncbi.nlm.nih.gov/geo/series/GSE118nnn/GSE118546/suppl/GSE118546_macaque_fovea_all_10X_Jan2018.Rdata.gz
wget $url -O primate_data/GSE118546_macaque_fovea_all_10X_Jan2018.Rdata.gz
gunzip primate_data/*
install.packages( c('devtools', 'roxygen2') )
library(devtools)
library(roxygen2)
install_github( 'hb-gitified/cellrangerRkit',
                auth_token = 'your_token' )
macaque_fovea_seurat
An object of class Seurat 
30039 features across 111993 samples within 1 assay 
Active assay: RNA (30039 features, 0 variable features)

Cleanup

rm(human.count_matrix, human.gene_names, human.sample_annotations)
rm(count_matrix_URL, gene_names_URL, sample_annotations_URL, import_remote_data)
rm(mouse.data)
rm(Count.mat_fovea, macaque_fovea)

Combine

# combine
ret.list <- list(human = human_ret_seurat, mouse = mouse_ret_seurat, macaque = macaque_fovea_seurat)

# preprocess
ret.list <- lapply(X = ret.list, FUN = function(x) {
    x <- NormalizeData(x, verbose = FALSE)
    x <- FindVariableFeatures(x, selection.method = "vst", nfeatures = 2000, verbose = FALSE)
})

# cleanup
rm(human_ret_seurat, mouse_ret_seurat, macaque_fovea_seurat)

Integration

plan("multiprocess", workers = 4)
ret.anchors <- FindIntegrationAnchors(object.list = ret.list, dims = 1:50,  anchor.features = 1000)
plan("multiprocess", workers = 1)
ret.combined <- IntegrateData(anchorset = ret.anchors, dims = 1:50)

Integrated Analysis

plan("multiprocess", workers = 4)

DefaultAssay(ret.combined) <- "integrated"

# Run the standard workflow for visualization and clustering
ret.combined <- ScaleData(ret.combined, verbose = FALSE)
ret.combined <- RunPCA(ret.combined, npcs = 50, verbose = FALSE)
# t-SNE and Clustering
ret.combined <- RunUMAP(ret.combined, reduction = "pca", dims = 1:35)
ret.combined <- FindNeighbors(ret.combined, reduction = "pca", dims = 1:35)
ret.combined <- FindClusters(ret.combined, resolution = 0.075)

UMAP Visualization

DimPlot(ret.combined, reduction = "umap", group.by = "orig.ident")

DimPlot(ret.combined, reduction = "umap", label = TRUE)

DimPlot(ret.combined, reduction = "umap", split.by = "orig.ident", ncol = 1)

Identify Clusters with Canonical Markers

DefaultAssay(ret.combined) <- "RNA"

features <- tolower(c("Pde6a","Gnat2","Nefl","Camk2b","Thy1","Gad1","Slc6a9",
                      "Pcsk6","Trpm1","Sept4","Glul","Arr3","C1qa","Tm4sf1", "Mgp"))

FeaturePlot(object = ret.combined, 
            features = features, 
            pt.size = 0.1,
            cols = c("lightgrey", "#F26969"),
            min.cutoff = "q9",
            combine = TRUE) & NoLegend() & NoAxes()


# Cowplot method: make sure to change to "combine = FALSE" and remove "& NoLegend() & NoAxes"

# for(i in 1:length(p)) {
#   p[[i]] <- p[[i]] + NoLegend() + NoAxes()
# }
# 
# cowplot::plot_grid(plotlist = p, ncol=3)

Markers were determined from this paper and other sources.

Find Differentially Expressed Genes

cells.types <- c("Rod", "BC", "MG", "RGC", "CC", "AC", "VC", "HC", "M")
theme_set(theme_cowplot())

cell_type_avg <- function(seurat.combined, ident) {
  cells.x <- subset(seurat.combined, idents = ident)
  Idents(cells.x) <- "orig.ident"
  cells.x.avg <- log1p(AverageExpression(cells.x, verbose = FALSE)$RNA)
  cells.x.avg$gene <- rownames(cells.x.avg)
  return(cells.x.avg)
}

cells.plot <- as.list(cells.types)
cells.plot <- lapply(cells.plot, FUN = function(x) {
  cells.x.avg <- cell_type_avg(ret.combined, ident = x)
  x <- ggplot(cells.x.avg, aes(human_ret, mouse_ret)) + geom_point(size = 0.1) + ggtitle(x)
  return(x)
})

# For individual plots
# for (p in cells.plot) {
#   print(p)
# }

# For grid plot
cowplot::plot_grid(plotlist = cells.plot, ncol = 3)

ret.combined$celltype.organism <- paste(Idents(ret.combined), ret.combined$orig.ident, sep = "_")
ret.combined$celltype <- Idents(ret.combined)
Idents(ret.combined) <- "celltype.organism"

Tables with the most differentially expressed genes in each cell subtype:

for(i in seq_along(cells.diffgenes)) {
  print(knitr::kable(head(cells.diffgenes[[i]]),caption=cells.types[[i]]))
}

Rod
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
ckb 0 1.4493770 0.918 0.724 0 Inf Rod ckb
hsp90aa1 0 1.3457646 0.854 0.627 0 Inf Rod hsp90aa1
nrl 0 1.3140138 0.874 0.635 0 Inf Rod nrl
0610009b22rik 0 -0.6622860 0.000 0.130 0 Inf Rod 0610009b22rik
gm17018 0 -0.6831275 0.000 0.130 0 Inf Rod gm17018
spata1 0 -0.6929677 0.000 0.132 0 Inf Rod spata1
BC
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
neat1 0 3.086391 0.793 0.064 0 Inf BC neat1
mtch1 0 -1.305054 0.000 0.459 0 Inf BC mtch1
selenom 0 -1.338108 0.000 0.480 0 Inf BC selenom
araf 0 -1.342891 0.013 0.494 0 Inf BC araf
klc3 0 -1.424615 0.002 0.500 0 Inf BC klc3
pea15a 0 -1.427543 0.000 0.500 0 Inf BC pea15a
MG
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
tf 0 5.089073 0.962 0.000 0 Inf MG tf
spp1 0 3.879036 0.847 0.003 0 Inf MG spp1
crabp1 0 3.865908 0.876 0.028 0 Inf MG crabp1
gpx3 0 3.736219 0.869 0.052 0 Inf MG gpx3
ftl 0 3.672007 0.877 0.000 0 Inf MG ftl
actg1 0 3.639157 0.905 0.026 0 Inf MG actg1
RGC
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
malat1 0e+00 -5.358199 0.000 1.000 0.0000020 23.73532 RGC malat1
gm42418 0e+00 -5.948091 0.000 0.957 0.0000083 22.29749 RGC gm42418
ay036118 0e+00 -2.770196 0.000 0.826 0.0004565 18.29374 RGC ay036118
neat1 0e+00 5.237230 0.889 0.087 0.0007506 17.79653 RGC neat1
linc00599 1e-07 2.669694 0.815 0.000 0.0024960 16.59495 RGC linc00599
kcnq1ot1 1e-07 2.290579 0.926 0.261 0.0025965 16.55548 RGC kcnq1ot1
CC
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
gm42418 0 -5.444663 0.000 1.000 0 185.7925 CC gm42418
malat1 0 -5.893437 0.000 1.000 0 185.7925 CC malat1
ay036118 0 -2.515711 0.000 0.943 0 170.6009 CC ay036118
gnat2 0 -2.832927 0.117 0.943 0 153.8304 CC gnat2
mir124a-1hg 0 -2.420164 0.000 0.869 0 151.9125 CC mir124a-1hg
gngt2 0 -3.274161 0.143 0.937 0 149.3963 CC gngt2
AC
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
gm42418 0 -5.868449 0 1.000 0 258.1168 AC gm42418
malat1 0 -6.139436 0 0.994 0 255.9481 AC malat1
ay036118 0 -2.993976 0 0.935 0 236.7899 AC ay036118
snhg11 0 -3.730037 0 0.916 0 230.5453 AC snhg11
ac149090.1 0 -2.375670 0 0.729 0 173.6489 AC ac149090.1
c230004f18rik 0 -2.693746 0 0.729 0 173.6488 AC c230004f18rik
VC
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
hla-b 0 3.429125 0.884 0.00 0 112.97262 VC hla-b
rps3a 0 2.938618 0.826 0.00 0 104.06885 VC rps3a
hla-e 0 3.220179 0.826 0.00 0 104.06878 VC hla-e
hla-a 0 3.030967 0.812 0.00 0 101.88369 VC hla-a
hla-c 0 2.913989 0.797 0.00 0 99.71476 VC hla-c
a2m 0 3.322554 0.797 0.01 0 96.28914 VC a2m
HC
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
gm42418 0 -6.046691 0.000 1.000 0 108.45517 HC gm42418
malat1 0 -5.904074 0.000 0.942 0 100.35094 HC malat1
neat1 0 4.028649 0.967 0.043 0 70.98094 HC neat1
ay036118 0 -2.922930 0.000 0.710 0 70.65422 HC ay036118
pvalb 0 3.437120 0.902 0.000 0 63.70192 HC pvalb
gpi1 0 -2.385417 0.000 0.580 0 55.70038 HC gpi1
M
p_val avg_logFC pct.1 pct.2 p_val_adj logp types genes
ftl 0 4.612295 0.98 0 0 80.75030 M ftl
hla-dra 0 4.664191 0.94 0 0 76.57279 M hla-dra
hla-a 0 2.899218 0.94 0 0 76.57279 M hla-a
hla-drb1 0 4.096260 0.92 0 0 74.52028 M hla-drb1
rps3a 0 3.397725 0.92 0 0 74.52028 M rps3a
hla-b 0 3.163581 0.92 0 0 74.52028 M hla-b

Save as csv files

for(i in seq_along(cells.diffgenes)) {
  write.csv(cells.diffgenes[[i]], sprintf("results/%d_%s.csv", i, cells.types[[i]]))
}
genes_to_plot <- 3
for (i in seq_along(cells.types)) {
  print(FeaturePlot(object = ret.combined, 
              features = rownames(cells.diffgenes[[i]])[1:genes_to_plot], 
              split.by = "orig.ident", 
              max.cutoff = 3, 
              cols = c("grey", "red"),
              pt.size = 0.07,
              combine = TRUE,
              label.size = 0.5
              ) + plot_annotation(title = cells.types[[i]]) & NoLegend() & NoAxes()
        )
}

Check cell proportion for each species:

knitr::kable(prop.table(x = table(Idents(ret.combined), ret.combined@meta.data$orig.ident), margin = 2))
human_ret macaque_fovea mouse_ret
0 0.2627047 0.1535810 0.2875831
1 0.5498980 0.0758172 0.3164080
2 0.0001493 0.1855205 0.0002217
3 0.0009955 0.1458216 0.0035477
4 0.0531581 0.0808176 0.0838137
5 0.0114977 0.0783888 0.0388027
6 0.0406650 0.0552267 0.0569845
7 0.0187148 0.0577625 0.0343681
8 0.0484794 0.0443242 0.0917960
9 0.0001493 0.0342075 0.0000000
10 0.0000498 0.0232247 0.0004435
11 0.0076154 0.0156081 0.0152993
12 0.0000000 0.0117775 0.0000000
13 0.0034344 0.0089827 0.0436807
14 0.0000000 0.0109203 0.0000000
15 0.0000000 0.0101435 0.0008869
16 0.0024887 0.0039645 0.0261641
17 0.0000000 0.0039110 0.0000000

Gene Enrichment Analysis

plot_enrichment <- function(type = "Rod", other = "") {
    file_path <- sprintf("enrich_data/%s_%s.txt", type, other)
    x <- read.table(file_path, header=T, sep="\t", skip = 11)
    colnames(x) <- gsub("upload_1..fold.Enrichment.", "Fold_Enrichment", colnames(x))
    colnames(x) <- gsub("upload_1..FDR.", "FDR", colnames(x))
    colnames(x) <- gsub("GO.biological.process.complete", "GO", colnames(x))
    colnames(x) <- gsub("Homo.sapiens...REFLIST..20851.", "Count", colnames(x))
    x$GO<- factor(x$GO, levels = x$GO[order(x$Fold_Enrichment, decreasing =F)])
    x <- x[order(x$FDR),]
    x <- x[1:10,]
    g<- ggplot(data=x, aes(x=Fold_Enrichment, y=GO, colour=FDR)) + 
        geom_point(aes(size=Count)) + 
        theme_bw() +
        theme(panel.background = element_rect(fill = NA) , 
              axis.ticks.x=element_blank(), 
              axis.text.y = element_text(size = 12) , 
              panel.grid.major = element_blank(), 
              panel.grid.minor = element_blank()) + 
        labs(x = " ", y =" ") +
        scale_colour_gradient(low = "red", high = "blue") +
        ggtitle(type)
    return(g)
}

for (type in cells.types) {
    print(plot_enrichment(type = type, other= "top200"))
    rm(type)
}

LS0tCnRpdGxlOiAiSW50ZWdyYXRpbmcgUHJpbWF0ZSBEYXRhIGludG8gQW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQotLS0KIyBTZXR1cApMb2FkIGxpYnJhcmllcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhdGNod29yaykKCiMgcGFyYWxsZWxpemF0aW9uCmxpYnJhcnkoZnV0dXJlKQpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemU9ICtJbmYpCnBsYW4oKQpgYGAKUHJvY2VzcyBIdW1hbiBEYXRhCmBgYHtyfQppbXBvcnRfcmVtb3RlX2RhdGEgPC0gZnVuY3Rpb24oZmlsZV91cmwsIHR5cGUgPSAidGFibGUiLCBoZWFkZXIgPSBGQUxTRSkgewogIGNvbiA8LSBnemNvbih1cmwoZmlsZV91cmwpKQogIHR4dCA8LSByZWFkTGluZXMoY29uKQogIGlmICh0eXBlID09ICJNTSIpIHsgcmV0dXJuIChyZWFkTU0odGV4dENvbm5lY3Rpb24odHh0KSkpIH0KICBpZiAodHlwZSA9PSAidGFibGUiKSB7IHJldHVybiAocmVhZC50YWJsZSh0ZXh0Q29ubmVjdGlvbih0eHQpLCBoZWFkZXIgPSBoZWFkZXIpKSB9Cn0KY291bnRfbWF0cml4X1VSTCA8LSAiaHR0cHM6Ly9mdHAubmNiaS5ubG0ubmloLmdvdi9nZW8vc2VyaWVzL0dTRTEzN25ubi9HU0UxMzc1Mzcvc3VwcGwvR1NFMTM3NTM3X2NvdW50cy5tdHguZ3oiCmdlbmVfbmFtZXNfVVJMIDwtICJodHRwczovL2Z0cC5uY2JpLm5sbS5uaWguZ292L2dlby9zZXJpZXMvR1NFMTM3bm5uL0dTRTEzNzUzNy9zdXBwbC9HU0UxMzc1MzdfZ2VuZV9uYW1lcy50eHQuZ3oiCnNhbXBsZV9hbm5vdGF0aW9uc19VUkwgPC0gImh0dHBzOi8vZnRwLm5jYmkubmxtLm5paC5nb3YvZ2VvL3Nlcmllcy9HU0UxMzdubm4vR1NFMTM3NTM3L3N1cHBsL0dTRTEzNzUzN19zYW1wbGVfYW5ub3RhdGlvbnMudHN2Lmd6IgoKaHVtYW4uY291bnRfbWF0cml4IDwtIGFzLm1hdHJpeChpbXBvcnRfcmVtb3RlX2RhdGEoY291bnRfbWF0cml4X1VSTCwgdHlwZSA9ICJNTSIpKQpodW1hbi5nZW5lX25hbWVzIDwtIGltcG9ydF9yZW1vdGVfZGF0YShnZW5lX25hbWVzX1VSTCwgdHlwZSA9ICJ0YWJsZSIpCmh1bWFuLnNhbXBsZV9hbm5vdGF0aW9ucyA8LSBpbXBvcnRfcmVtb3RlX2RhdGEoc2FtcGxlX2Fubm90YXRpb25zX1VSTCwgdHlwZSA9ICJ0YWJsZSIsIGhlYWRlciA9IFRSVUUpCmBgYApgYGB7cn0Kcm93bmFtZXMoaHVtYW4uY291bnRfbWF0cml4KSA8LSB0b2xvd2VyKGh1bWFuLmdlbmVfbmFtZXNbLDFdKQpjb2xuYW1lcyhodW1hbi5jb3VudF9tYXRyaXgpIDwtIHRvbG93ZXIoaHVtYW4uc2FtcGxlX2Fubm90YXRpb25zWywxXSkKCmh1bWFuX3JldF9zZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGh1bWFuLmNvdW50X21hdHJpeCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGEuZGF0YSA9IGh1bWFuLnNhbXBsZV9hbm5vdGF0aW9ucywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAiaHVtYW5fcmV0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCmh1bWFuX3JldF9zZXVyYXQKYGBgCgpQcm9jZXNzIE1vdXNlIERhdGEKYGBge3J9Cm1vdXNlLmRhdGEgPC0gUmVhZDEwWChkYXRhLmRpciA9ICJmaWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeCIpCmRpbW5hbWVzKG1vdXNlLmRhdGEpW1sxXV0gPC0gdG9sb3dlcihkaW1uYW1lcyhtb3VzZS5kYXRhKVtbMV1dKQpkaW1uYW1lcyhtb3VzZS5kYXRhKVtbMl1dIDwtIHRvbG93ZXIoZGltbmFtZXMobW91c2UuZGF0YSlbWzJdXSkKbW91c2VfcmV0X3NldXJhdCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gbW91c2UuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAibW91c2VfcmV0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCm1vdXNlX3JldF9zZXVyYXQKYGBgCgpQcm9jZXNzIFByaW1hdGUgRGF0YQpgYGB7YmFzaH0KdXJsPWh0dHBzOi8vZnRwLm5jYmkubmxtLm5paC5nb3YvZ2VvL3Nlcmllcy9HU0UxMThubm4vR1NFMTE4NTQ2L3N1cHBsL0dTRTExODU0Nl9tYWNhcXVlX2ZvdmVhX2FsbF8xMFhfSmFuMjAxOC5SZGF0YS5negp3Z2V0ICR1cmwgLU8gcHJpbWF0ZV9kYXRhL0dTRTExODU0Nl9tYWNhcXVlX2ZvdmVhX2FsbF8xMFhfSmFuMjAxOC5SZGF0YS5negpndW56aXAgcHJpbWF0ZV9kYXRhLyoKYGBgCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCBjKCdkZXZ0b29scycsICdyb3h5Z2VuMicpICkKbGlicmFyeShkZXZ0b29scykKbGlicmFyeShyb3h5Z2VuMikKaW5zdGFsbF9naXRodWIoICdoYi1naXRpZmllZC9jZWxscmFuZ2VyUmtpdCcsCiAgICAgICAgICAgICAgICBhdXRoX3Rva2VuID0gJ3lvdXJfdG9rZW4nICkKYGBgCmBgYHtyfQpsb2FkKCJwcmltYXRlX2RhdGEvR1NFMTE4NTQ2X21hY2FxdWVfZm92ZWFfYWxsXzEwWF9KYW4yMDE4LlJkYXRhIikKCmRpbW5hbWVzKENvdW50Lm1hdF9mb3ZlYSlbWzFdXSA8LSB0b2xvd2VyKGRpbW5hbWVzKENvdW50Lm1hdF9mb3ZlYSlbWzFdXSkKbWFjYXF1ZV9mb3ZlYV9zZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KENvdW50Lm1hdF9mb3ZlYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAibWFjYXF1ZV9mb3ZlYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCgojIGdpdmUgbWFjYXF1ZSBkdGEgdW5pZm9ybSBuYW1lIGluICJvcmlnLmlkZW50IiBtZXRhZGF0YSBjb2x1bW4KQWRkTWV0YURhdGEobWFjYXF1ZV9mb3ZlYV9zZXVyYXQsIAogICAgICAgICAgICBtZXRhZGF0YSA9IG1hY2FxdWVfZm92ZWFfc2V1cmF0W1sib3JpZy5pZGVudCJdXSwgCiAgICAgICAgICAgIGNvbC5uYW1lID0gIm9yaWcuc2FtcGxlLm5hbWUiKQptYWNhcXVlX2ZvdmVhX3NldXJhdFtbIm9yaWcuaWRlbnQiXV0gPC0gIm1hY2FxdWVfZm92ZWEiCgptYWNhcXVlX2ZvdmVhX3NldXJhdApgYGAKQ2xlYW51cApgYGB7cn0Kcm0oaHVtYW4uY291bnRfbWF0cml4LCBodW1hbi5nZW5lX25hbWVzLCBodW1hbi5zYW1wbGVfYW5ub3RhdGlvbnMpCnJtKGNvdW50X21hdHJpeF9VUkwsIGdlbmVfbmFtZXNfVVJMLCBzYW1wbGVfYW5ub3RhdGlvbnNfVVJMLCBpbXBvcnRfcmVtb3RlX2RhdGEpCnJtKG1vdXNlLmRhdGEpCnJtKENvdW50Lm1hdF9mb3ZlYSwgbWFjYXF1ZV9mb3ZlYSkKYGBgCgoKQ29tYmluZQpgYGB7cn0KIyBjb21iaW5lCnJldC5saXN0IDwtIGxpc3QoaHVtYW4gPSBodW1hbl9yZXRfc2V1cmF0LCBtb3VzZSA9IG1vdXNlX3JldF9zZXVyYXQsIG1hY2FxdWUgPSBtYWNhcXVlX2ZvdmVhX3NldXJhdCkKCiMgcHJlcHJvY2VzcwpyZXQubGlzdCA8LSBsYXBwbHkoWCA9IHJldC5saXN0LCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgICB4IDwtIE5vcm1hbGl6ZURhdGEoeCwgdmVyYm9zZSA9IEZBTFNFKQogICAgeCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyh4LCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDAsIHZlcmJvc2UgPSBGQUxTRSkKfSkKCiMgY2xlYW51cApybShodW1hbl9yZXRfc2V1cmF0LCBtb3VzZV9yZXRfc2V1cmF0LCBtYWNhcXVlX2ZvdmVhX3NldXJhdCkKYGBgCgojIEludGVncmF0aW9uCmBgYHtyfQpwbGFuKCJtdWx0aXByb2Nlc3MiLCB3b3JrZXJzID0gNCkKcmV0LmFuY2hvcnMgPC0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IHJldC5saXN0LCBkaW1zID0gMTo1MCwgIGFuY2hvci5mZWF0dXJlcyA9IDEwMDApCnBsYW4oIm11bHRpcHJvY2VzcyIsIHdvcmtlcnMgPSAxKQpyZXQuY29tYmluZWQgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSByZXQuYW5jaG9ycywgZGltcyA9IDE6NTApCmBgYAoKIyBJbnRlZ3JhdGVkIEFuYWx5c2lzCmBgYHtyfQpwbGFuKCJtdWx0aXByb2Nlc3MiLCB3b3JrZXJzID0gNCkKCkRlZmF1bHRBc3NheShyZXQuY29tYmluZWQpIDwtICJpbnRlZ3JhdGVkIgoKIyBSdW4gdGhlIHN0YW5kYXJkIHdvcmtmbG93IGZvciB2aXN1YWxpemF0aW9uIGFuZCBjbHVzdGVyaW5nCnJldC5jb21iaW5lZCA8LSBTY2FsZURhdGEocmV0LmNvbWJpbmVkLCB2ZXJib3NlID0gRkFMU0UpCnJldC5jb21iaW5lZCA8LSBSdW5QQ0EocmV0LmNvbWJpbmVkLCBucGNzID0gNTAsIHZlcmJvc2UgPSBGQUxTRSkKIyB0LVNORSBhbmQgQ2x1c3RlcmluZwpyZXQuY29tYmluZWQgPC0gUnVuVU1BUChyZXQuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMTozNSkKcmV0LmNvbWJpbmVkIDwtIEZpbmROZWlnaGJvcnMocmV0LmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MzUpCnJldC5jb21iaW5lZCA8LSBGaW5kQ2x1c3RlcnMocmV0LmNvbWJpbmVkLCByZXNvbHV0aW9uID0gMC4wNzUpCmBgYAojIFVNQVAgVmlzdWFsaXphdGlvbgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpEaW1QbG90KHJldC5jb21iaW5lZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKRGltUGxvdChyZXQuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSAzfQpEaW1QbG90KHJldC5jb21iaW5lZCwgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IiwgbmNvbCA9IDEpCmBgYAoKIyBJZGVudGlmeSBDbHVzdGVycyB3aXRoIENhbm9uaWNhbCBNYXJrZXJzCmBgYHtyfQpEZWZhdWx0QXNzYXkocmV0LmNvbWJpbmVkKSA8LSAiUk5BIgoKZmVhdHVyZXMgPC0gdG9sb3dlcihjKCJQZGU2YSIsIkduYXQyIiwiTmVmbCIsIkNhbWsyYiIsIlRoeTEiLCJHYWQxIiwiU2xjNmE5IiwKICAgICAgICAgICAgICAgICAgICAgICJQY3NrNiIsIlRycG0xIiwiU2VwdDQiLCJHbHVsIiwiQXJyMyIsIkMxcWEiLCJUbTRzZjEiLCAiTWdwIikpCgpGZWF0dXJlUGxvdChvYmplY3QgPSByZXQuY29tYmluZWQsIAogICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmVzLCAKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMSwKICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Z3JleSIsICIjRjI2OTY5IiksCiAgICAgICAgICAgIG1pbi5jdXRvZmYgPSAicTkiLAogICAgICAgICAgICBjb21iaW5lID0gVFJVRSkgJiBOb0xlZ2VuZCgpICYgTm9BeGVzKCkKYGBgCgoqIFJvZCA6IHBkZTZhCiogQUMgKGFtYWNyaW5lIGNlbGwpIDogZ2FkMSwgc2xjNmE5CiogTUcgKE3DvGxsZXIgZ2xpYSkgOiBnbHVsCiogQkMgKGJpcG9sYXIgY2VsbCkgOiBUcnBtLCBjYW1rMmIKKiBDQyAoY29uZSBjZWxsKSA6IGduYXQyLCBhcnIzCiogUkdDIChyZXRpbmFsIGdhbmdsaWFsIGNlbGwpIDogbmVmbCwgdGh5MQoqIFZDICh2YXNjdWxhciBjZWxsKSA6IG1ncCwgdG00c2YxCiogTSAobWljcm9nbGlhKSA6IGMxcWEKKiBIQyAoaG9yaXpvbnRhbCBjZWxsKSA6IHNlcHQ0CgpNYXJrZXJzIHdlcmUgZGV0ZXJtaW5lZCBmcm9tIFt0aGlzXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTQ2Ny0wMTktMTI3ODAtOCkgcGFwZXIgYW5kIG90aGVyIHNvdXJjZXMuCmBgYHtyfQpyZXQuY29tYmluZWQgPC0gUmVuYW1lSWRlbnRzKHJldC5jb21iaW5lZCwgYDBgID0gIk1HIiwgYDFgID0gIlJvZCIsIGAyYCA9ICJSR0MiLCAKICAgIGAzYCA9ICJSR0MiLCBgNGAgPSAiQkMiLCBgNWAgPSAiQ0MiLCBgNmAgPSAiQkMiLCBgN2AgPSAiQUMiLCBgOGAgPSAiQkMiLCBgOWAgPSAiUkdDIiwgCiAgICBgMTBgID0gIlJHQyIsIGAxMWA9ICJIQyIsIGAxMmAgPSAiTUciLCBgMTNgID0gIlZDIiwgYDE0YCA9ICJSR0MiLCBgMTVgID0gIlJHQyIsIGAxNmAgPSAiTSIsIGAxN2AgPSAiUkdDIikKCkRpbVBsb3QocmV0LmNvbWJpbmVkLCBsYWJlbCA9IFRSVUUpCmBgYAoKCiMgRmluZCBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMKYGBge3J9CmNlbGxzLnR5cGVzIDwtIGMoIlJvZCIsICJCQyIsICJNRyIsICJSR0MiLCAiQ0MiLCAiQUMiLCAiVkMiLCAiSEMiLCAiTSIpCnRoZW1lX3NldCh0aGVtZV9jb3dwbG90KCkpCgpjZWxsX3R5cGVfYXZnIDwtIGZ1bmN0aW9uKHNldXJhdC5jb21iaW5lZCwgaWRlbnQpIHsKICBjZWxscy54IDwtIHN1YnNldChzZXVyYXQuY29tYmluZWQsIGlkZW50cyA9IGlkZW50KQogIElkZW50cyhjZWxscy54KSA8LSAib3JpZy5pZGVudCIKICBjZWxscy54LmF2ZyA8LSBsb2cxcChBdmVyYWdlRXhwcmVzc2lvbihjZWxscy54LCB2ZXJib3NlID0gRkFMU0UpJFJOQSkKICBjZWxscy54LmF2ZyRnZW5lIDwtIHJvd25hbWVzKGNlbGxzLnguYXZnKQogIHJldHVybihjZWxscy54LmF2ZykKfQoKY2VsbHMucGxvdCA8LSBhcy5saXN0KGNlbGxzLnR5cGVzKQpjZWxscy5wbG90IDwtIGxhcHBseShjZWxscy5wbG90LCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgY2VsbHMueC5hdmcgPC0gY2VsbF90eXBlX2F2ZyhyZXQuY29tYmluZWQsIGlkZW50ID0geCkKICB4IDwtIGdncGxvdChjZWxscy54LmF2ZywgYWVzKGh1bWFuX3JldCwgbW91c2VfcmV0KSkgKyBnZW9tX3BvaW50KHNpemUgPSAwLjEpICsgZ2d0aXRsZSh4KQogIHJldHVybih4KQp9KQoKIyBGb3IgaW5kaXZpZHVhbCBwbG90cwojIGZvciAocCBpbiBjZWxscy5wbG90KSB7CiMgICBwcmludChwKQojIH0KCiMgRm9yIGdyaWQgcGxvdApjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBjZWxscy5wbG90LCBuY29sID0gMykKYGBgCmBgYHtyfQpyZXQuY29tYmluZWQkY2VsbHR5cGUub3JnYW5pc20gPC0gcGFzdGUoSWRlbnRzKHJldC5jb21iaW5lZCksIHJldC5jb21iaW5lZCRvcmlnLmlkZW50LCBzZXAgPSAiXyIpCnJldC5jb21iaW5lZCRjZWxsdHlwZSA8LSBJZGVudHMocmV0LmNvbWJpbmVkKQpJZGVudHMocmV0LmNvbWJpbmVkKSA8LSAiY2VsbHR5cGUub3JnYW5pc20iCmBgYApgYGB7cn0KY2VsbHMuZGlmZmdlbmVzIDwtIGFzLmxpc3QoY2VsbHMudHlwZXMpCmNlbGxzLmRpZmZnZW5lcyA8LSBsYXBwbHkoY2VsbHMuZGlmZmdlbmVzLCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgbGFiX2h1bWFuIDwtIHNwcmludGYoIiVzX2h1bWFuX3JldCIsIHgpCiAgbGFiX21vdXNlIDwtIHNwcmludGYoIiVzX21vdXNlX3JldCIsIHgpCiAgcmV0dXJuKEZpbmRNYXJrZXJzKHJldC5jb21iaW5lZCwgaWRlbnQuMSA9IGxhYl9odW1hbiwgaWRlbnQuMiA9IGxhYl9tb3VzZSkpCn0pCgoKZm9yKGkgaW4gc2VxX2Fsb25nKGNlbGxzLmRpZmZnZW5lcykpIHsKCXggPC0gY2VsbHMuZGlmZmdlbmVzW1tpXV0KCXggPC0gY2JpbmQoeCwgbG9ncCA9IC1sb2coeCRwX3ZhbCksIHR5cGVzID0gY2VsbHMudHlwZXNbW2ldXSwgZ2VuZXMgPSByb3duYW1lcyh4KSkKCXggPC0geFshZ3JlcGwoIm10LSIsIHgkZ2VuZXMpLF0gIyByZW1vdmUgbWl0b2Nob25kcmlhbCBnZW5lcwoJY2VsbHMuZGlmZmdlbmVzW1tpXV0gPC0geAoJcm0oeCkKfQpgYGAKVGFibGVzIHdpdGggdGhlIG1vc3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGluIGVhY2ggY2VsbCBzdWJ0eXBlOgpgYGB7cn0KZm9yKGkgaW4gc2VxX2Fsb25nKGNlbGxzLmRpZmZnZW5lcykpIHsKICBwcmludChrbml0cjo6a2FibGUoaGVhZChjZWxscy5kaWZmZ2VuZXNbW2ldXSksY2FwdGlvbj1jZWxscy50eXBlc1tbaV1dKSkKfQpgYGAKU2F2ZSBhcyBjc3YgZmlsZXMKYGBge3J9CmZvcihpIGluIHNlcV9hbG9uZyhjZWxscy5kaWZmZ2VuZXMpKSB7CiAgd3JpdGUuY3N2KGNlbGxzLmRpZmZnZW5lc1tbaV1dLCBzcHJpbnRmKCJyZXN1bHRzLyVkXyVzLmNzdiIsIGksIGNlbGxzLnR5cGVzW1tpXV0pKQp9CmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KZ2VuZXNfdG9fcGxvdCA8LSAzCmZvciAoaSBpbiBzZXFfYWxvbmcoY2VsbHMudHlwZXMpKSB7CiAgcHJpbnQoRmVhdHVyZVBsb3Qob2JqZWN0ID0gcmV0LmNvbWJpbmVkLCAKICAgICAgICAgICAgICBmZWF0dXJlcyA9IHJvd25hbWVzKGNlbGxzLmRpZmZnZW5lc1tbaV1dKVsxOmdlbmVzX3RvX3Bsb3RdLCAKICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IiwgCiAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDMsIAogICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5IiwgInJlZCIpLAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjA3LAogICAgICAgICAgICAgIGNvbWJpbmUgPSBUUlVFLAogICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLjUKICAgICAgICAgICAgICApICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gY2VsbHMudHlwZXNbW2ldXSkgJiBOb0xlZ2VuZCgpICYgTm9BeGVzKCkKICAgICAgICApCn0KYGBgCgpDaGVjayBjZWxsIHByb3BvcnRpb24gZm9yIGVhY2ggc3BlY2llczoKYGBge3J9CmtuaXRyOjprYWJsZShwcm9wLnRhYmxlKHggPSB0YWJsZShJZGVudHMocmV0LmNvbWJpbmVkKSwgcmV0LmNvbWJpbmVkQG1ldGEuZGF0YSRvcmlnLmlkZW50KSwgbWFyZ2luID0gMikpCmBgYAoKIyBHZW5lIEVucmljaG1lbnQgQW5hbHlzaXMKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShkYXRhLnRhYmxlKQpjZWxscy5kaWZmZ2VuZXMuY29tYmluZWQgPC0gcmJpbmRsaXN0KGNlbGxzLmRpZmZnZW5lcykKCiMgUHJlcHJvY2Vzc2luZwojIGZvcihpIGluIHNlcV9hbG9uZyhjZWxscy5kaWZmZ2VuZXMuY29tYmluZWQpKSB7CiMgCWlmIChjZWxscy5kaWZmZ2VuZXMuY29tYmluZWRbaV0kbG9ncCA+IDc1MCkgewojIAkJY2VsbHMuZGlmZmdlbmVzLmNvbWJpbmVkW2ldJGxvZ3AgPC0gNzQ5CiMgCX0KIyAJaWYgKGNlbGxzLmRpZmZnZW5lcy5jb21iaW5lZFtpXSRhdmdfbG9nRkMgPiAzKSB7CiMgCQljZWxscy5kaWZmZ2VuZXMuY29tYmluZWRbaV0kYXZnX2xvZ0ZDIDwtIDIuOTkKIyAJfQojIH0KIyAKIyBjZWxscy5kaWZmZ2VuZXMuY29tYmluZWQkbG9ncCA8LSBnc3ViKCJJbmYiLCA3NDksIGNlbGxzLmRpZmZnZW5lcy5jb21iaW5lZCRsb2dwKQoKZ2dwbG90KGRhdGE9Y2VsbHMuZGlmZmdlbmVzLmNvbWJpbmVkLCAKCQkgICBhZXMoeD1hdmdfbG9nRkMseT1sb2dwLCBjb2xvdXI9dHlwZXMsIGxhYmVsID0gZ2VuZXMpKSArIAoJZ2VvbV9wb2ludChzaXplPTAuMikgKyAKCXRoZW1lX2J3KCkgKyAKCXRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSwgCgkJICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksICAKCQkgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIAoJCSAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCgkJICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIAoJbGFicyh4ID0gImxvZzIoRm9sZCBjaGFuZ2VzKVxuKDNLL1dUKSIsIHkgPSItbG9nMTAocCB2YWx1ZSkiKSArCglzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoLTMsIDMpKSArCglzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMSwgNzUwKSkgKwoJZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAxLCBjb2xvdXI9ImdyZXkiLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjcgKSArCglnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9IDAgLCBjb2xvdXI9ImdyZXkiLCAgIHNpemU9MC43KQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDYsIGRwaSA9IDQwMCwgd2FybmluZz1GQUxTRX0KcGxvdF9lbnJpY2htZW50IDwtIGZ1bmN0aW9uKHR5cGUgPSAiUm9kIiwgb3RoZXIgPSAiIikgewoJZmlsZV9wYXRoIDwtIHNwcmludGYoImVucmljaF9kYXRhLyVzXyVzLnR4dCIsIHR5cGUsIG90aGVyKQoJeCA8LSByZWFkLnRhYmxlKGZpbGVfcGF0aCwgaGVhZGVyPVQsIHNlcD0iXHQiLCBza2lwID0gMTEpCgljb2xuYW1lcyh4KSA8LSBnc3ViKCJ1cGxvYWRfMS4uZm9sZC5FbnJpY2htZW50LiIsICJGb2xkX0VucmljaG1lbnQiLCBjb2xuYW1lcyh4KSkKCWNvbG5hbWVzKHgpIDwtIGdzdWIoInVwbG9hZF8xLi5GRFIuIiwgIkZEUiIsIGNvbG5hbWVzKHgpKQoJY29sbmFtZXMoeCkgPC0gZ3N1YigiR08uYmlvbG9naWNhbC5wcm9jZXNzLmNvbXBsZXRlIiwgIkdPIiwgY29sbmFtZXMoeCkpCgljb2xuYW1lcyh4KSA8LSBnc3ViKCJIb21vLnNhcGllbnMuLi5SRUZMSVNULi4yMDg1MS4iLCAiQ291bnQiLCBjb2xuYW1lcyh4KSkKCXgkR088LSBmYWN0b3IoeCRHTywgbGV2ZWxzID0geCRHT1tvcmRlcih4JEZvbGRfRW5yaWNobWVudCwgZGVjcmVhc2luZyA9RildKQoJeCA8LSB4W29yZGVyKHgkRkRSKSxdCgl4IDwtIHhbMToxMCxdCglnPC0gZ2dwbG90KGRhdGE9eCwgYWVzKHg9Rm9sZF9FbnJpY2htZW50LCB5PUdPLCBjb2xvdXI9RkRSKSkgKyAKCQlnZW9tX3BvaW50KGFlcyhzaXplPUNvdW50KSkgKyAKCQl0aGVtZV9idygpICsKCQl0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSkgLCAKCQkJICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCAKCQkJICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpICwgCgkJCSAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCgkJCSAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKCQlsYWJzKHggPSAiICIsIHkgPSIgIikgKwoJCXNjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAicmVkIiwgaGlnaCA9ICJibHVlIikgKwoJCWdndGl0bGUodHlwZSkKCXJldHVybihnKQp9Cgpmb3IgKHR5cGUgaW4gY2VsbHMudHlwZXMpIHsKCXByaW50KHBsb3RfZW5yaWNobWVudCh0eXBlID0gdHlwZSwgb3RoZXI9ICJ0b3AyMDAiKSkKCXJtKHR5cGUpCn0KYGBgCgo=